System Specification

Table of Contents

System Overview

The Elevator Simulation System is a multi-elevator control system that manages the operation of two elevators serving floors -1, 1, 2, and 3 (note: floor 0 does not exist). The system handles user requests for calling elevators from floors and selecting destination floors from within elevators, implementing efficient dispatching algorithms to optimize service time.

System Architecture

The system follows a layered architecture with clear separation of concerns:

Backend Layer

Communication Layer

Class Diagram

classDiagram class Simulator { +Optional~ElevatorAPI~ api +List~Elevator~ elevators +Optional~Dispatcher~ dispatcher +__init__() void +set_api_and_initialize_components(api: ElevatorAPI) void +update() void +reset() void } class ElevatorAPI { +Optional~Simulator~ world +ZmqClientThread zmq_client +__init__(world: Optional~Simulator~, zmq_ip: str, zmq_port: str) void +set_world(world: Simulator) void +stop() void +_parse_and_execute(command: str) Optional~str~ +_handle_call_elevator(floor: int, direction: str) Dict~str, Any~ +_handle_select_floor(floor: int, elevator_id: int) Dict~str, Any~ +_handle_open_door(elevator_id: int) Dict~str, Any~ +_handle_close_door(elevator_id: int) Dict~str, Any~ +_handle_reset() Dict~str, Any~ +send_door_opened_message(elevator_id: int) void +send_door_closed_message(elevator_id: int) void +send_floor_arrived_message(elevator_id: int, floor: int, direction: Optional~MoveDirection~) void +fetch_states() List~Dict~str, Any~~ +ui_call_elevator(data: Dict~str, Any~) str +ui_select_floor(data: Dict~str, Any~) str +ui_open_door(data: Dict~str, Any~) str +ui_close_door(data: Dict~str, Any~) str } class Dispatcher { +Simulator world +ElevatorAPI api +Dict~str, Call~ pending_calls +__init__(world: Simulator, api: ElevatorAPI) void +add_call(floor: int, direction: str) void +add_outside_call(floor: int, direction: Optional~MoveDirection~) str +assign_task(elevator_idx: int, floor: int, call_id: Optional~str~) void +get_call_direction(call_id: str) Optional~MoveDirection~ +complete_call(call_id: str) void +update() void +reset() void -_process_pending_calls() void -_optimize_task_queue(elevator: Elevator) void -_get_elevator_committed_direction(elevator: Elevator) Optional~MoveDirection~ -_can_elevator_serve_call(elevator: Elevator, floor: int, direction: Optional~MoveDirection~) bool } class Elevator { +int id +Simulator world +ElevatorAPI api +int current_floor +int previous_floor +List~Task~ task_queue +ElevatorState state +DoorState door_state +Optional~MoveDirection~ direction +float last_state_change +float last_door_change +Optional~float~ moving_since +bool floor_changed +bool floor_arrival_announced +Optional~float~ arrival_time +bool serviced_current_arrival +__init__(elevator_id: int, world: Simulator, api: ElevatorAPI) void +update() void +request_movement_if_needed() void +open_door() void +close_door() void +calculate_estimated_time(floor: int, direction: Optional~MoveDirection~) float +reset() void -_set_floor(new_floor: int) void -_set_moving_state(direction_value: str) void -_is_moving() bool -_get_movement_direction() int -_determine_direction() void -_handle_arrival_at_target_floor(current_time: float) void } class Task { +int floor +Optional~str~ call_id +__init__(floor: int, call_id: Optional~str~) void +is_outside_call() bool } class Call { +str call_id +int floor +Optional~MoveDirection~ direction +CallState state +Optional~int~ assigned_elevator +__init__(call_id: str, floor: int, direction: Optional~MoveDirection~) void +assign_to_elevator(elevator_idx: int) void +complete() void +is_pending() bool +is_assigned() bool +is_completed() bool } class ElevatorState { <<enumeration>> IDLE MOVING_UP MOVING_DOWN } class DoorState { <<enumeration>> OPEN CLOSED OPENING CLOSING } class MoveDirection { <<enumeration>> UP DOWN } class CallState { <<enumeration>> PENDING ASSIGNED COMPLETED } Simulator *-- "2" Elevator : manages Simulator *-- "1" Dispatcher : manages Simulator o-- "1" ElevatorAPI : uses Dispatcher o-- Simulator : world reference Dispatcher o-- ElevatorAPI : api reference Dispatcher *-- "*" Call : manages Elevator o-- Simulator : world reference Elevator o-- ElevatorAPI : api reference Elevator *-- "*" Task : task_queue Elevator --> ElevatorState : state Elevator --> DoorState : door_state Elevator --> MoveDirection : direction Task --> Call : references via call_id Call --> MoveDirection : direction Call --> CallState : state ElevatorAPI o-- Simulator : world reference

Core Components

Simulator

The central orchestrator that manages all simulation entities and coordinates the main update loop.

ElevatorAPI

Central interface handling external ZMQ communication and delegating commands to appropriate components.

Dispatcher

Manages elevator call assignment using efficiency algorithms and optimizes task queues for each elevator.

Elevator

Represents individual elevator units with autonomous operation including movement, door control, and task processing.

Method Specifications

Simulator Class

__init__(self) -> None

Functional Description: Initializes the simulator with empty references. Components are initialized later via set_api_and_initialize_components.

set_api_and_initialize_components(self, api: ElevatorAPI) -> None

Functional Description: Sets the ElevatorAPI instance and initializes dependent components (elevators and dispatcher) that require API access for communication.

update(self) -> None

Functional Description: Main update loop that processes the simulation state by updating all elevators and the dispatcher.

reset(self) -> None

Functional Description: Resets all simulation components to their initial state.

ElevatorAPI Class

__init__(self, world: Optional[Simulator], zmq_ip: str = "127.0.0.1", zmq_port: str = "19982") -> None

Functional Description: Initializes the API with ZMQ communication thread and world reference.

stop(self) -> None

Functional Description: Stops the ZMQ communication thread and performs any necessary cleanup.

_parse_and_execute(self, command: str) -> Optional[str]

Functional Description: Parses incoming ZMQ commands and delegates to appropriate handler methods.

_handle_call_elevator(self, floor: int, direction: str) -> Dict[str, Any]

Functional Description: Processes elevator call requests from floors by delegating to dispatcher.

_handle_select_floor(self, floor: int, elevator_id: int) -> Dict[str, Any]

Functional Description: Processes floor selection requests from within elevators.

_handle_open_door(self, elevator_id: int) -> Dict[str, Any]

Functional Description: Manually opens elevator doors if conditions allow.

_handle_close_door(self, elevator_id: int) -> Dict[str, Any]

Functional Description: Manually closes elevator doors if conditions allow.

send_door_opened_message(self, elevator_id: int) -> None

Functional Description: Sends door_opened#elevator_id message via ZMQ.

send_door_closed_message(self, elevator_id: int) -> None

Functional Description: Sends door_closed#elevator_id message via ZMQ.

send_floor_arrived_message(self, elevator_id: int, floor: int, direction: Optional[MoveDirection]) -> None

Functional Description: Sends floor arrival messages with appropriate direction prefix.

fetch_states(self) -> List[Dict[str, Any]]

Functional Description: Returns current state of all elevators for frontend synchronization. Each dictionary in the list represents an elevator and contains keys such as elevator_id, floor, state (e.g., "IDLE", "MOVING_UP"), door_state (e.g., "OPEN", "CLOSED"), direction (e.g., "UP", "DOWN", "none"), target_floors (a list of floor numbers), and target_floors_origin (a dictionary mapping floor numbers to "outside" or "inside").

set_world(self, world: Simulator) -> None

Functional Description: Updates the world reference in the API instance. This is useful if the Simulator instance is not available at the time of ElevatorAPI initialization.

stop(self) -> None

Functional Description: Stops the ZMQ client thread gracefully, ensuring all connections are closed and resources are released. This is typically called during application shutdown.

ui_call_elevator(self, data: Dict[str, Any]) -> str

Functional Description: Handles 'call elevator' requests originating from the UI (likely via WebSocketBridge). Parses floor and direction from the data dictionary and delegates to the internal _handle_call_elevator method. Returns a JSON string indicating the outcome of the operation (success or error).

ui_select_floor(self, data: Dict[str, Any]) -> str

Functional Description: Handles 'select floor' requests originating from the UI. Parses floor and elevatorId from the data dictionary and delegates to the internal _handle_select_floor method. Returns a JSON string indicating the outcome.

ui_open_door(self, data: Dict[str, Any]) -> str

Functional Description: Handles 'open door' requests originating from the UI. Parses elevatorId from the data dictionary and delegates to the internal _handle_open_door method. Returns a JSON string indicating the outcome.

ui_close_door(self, data: Dict[str, Any]) -> str

Functional Description: Handles 'close door' requests originating from the UI. Parses elevatorId from the data dictionary and delegates to the internal _handle_close_door method. Returns a JSON string indicating the outcome.

Dispatcher Class

__init__(self, world: Simulator, api: ElevatorAPI) -> None

Functional Description: Initializes dispatcher with world and API references and empty pending calls dictionary.

add_call(self, floor: int, direction: str) -> None

Functional Description: Adds an outside call request and processes pending calls queue.

add_outside_call(self, floor: int, direction: Optional[MoveDirection]) -> str

Functional Description: Creates a new Call object with unique ID and adds to pending calls.

assign_task(self, elevator_idx: int, floor: int, call_id: Optional[str] = None) -> None

Functional Description: Assigns a task to a specific elevator, handling both outside calls (with call_id) and inside calls.

Supporting UML Sequence Diagram:

sequenceDiagram participant API as ElevatorAPI participant D as Dispatcher participant E as Elevator participant ZMQ as ZMQ Client API->>D: assign_task(elevator_idx, floor, call_id) D->>E: Check current_floor and door_state alt Already at floor with closed doors D->>API: send_floor_arrived_message() API->>ZMQ: floor_arrived message D->>E: open_door() D->>D: complete_call(call_id) else Need to add task D->>D: Check for duplicates D->>E: task_queue.append(Task) D->>D: _optimize_task_queue() alt Door is open D->>E: close_door() else Door is closed D->>E: request_movement_if_needed() end end

get_call_direction(self, call_id: str) -> Optional[MoveDirection]

Functional Description: Retrieves the direction for a pending call by call_id.

complete_call(self, call_id: str) -> None

Functional Description: Marks a call as completed and removes it from pending calls to free memory.

_process_pending_calls(self) -> None

Functional Description: Processes all pending calls by finding suitable elevators and assigning tasks.

_optimize_task_queue(self, elevator: Elevator) -> None

Functional Description: Optimizes elevator task queue using SCAN-like algorithm.

Supporting UML Sequence Diagram:

sequenceDiagram participant D as Dispatcher participant E as Elevator D->>E: Get current state and direction D->>D: Separate tasks above/below current floor alt Moving UP D->>D: Sort above tasks ascending D->>D: Sort below tasks ascending D->>E: task_queue = above + below else Moving DOWN D->>D: Sort below tasks descending D->>D: Sort above tasks ascending D->>E: task_queue = below + above else IDLE D->>D: Find closest task alt Closest is above D->>D: Set direction UP, optimize as moving up else Closest is below D->>D: Set direction DOWN, optimize as moving down end end

_get_elevator_committed_direction(self, elevator: Elevator) -> Optional[MoveDirection]

Functional Description: Determines the direction an elevator is committed to based on current state and task queue.

_can_elevator_serve_call(self, elevator: Elevator, floor: int, direction: Optional[MoveDirection]) -> bool

Functional Description: Determines if an elevator can serve an outside call.

Elevator Class

__init__(self, elevator_id: int, world: Simulator, api: ElevatorAPI) -> None

Functional Description: Initializes elevator with ID, world reference, API instance, and default state values.

update(self) -> None

Functional Description: Main update method called every simulation cycle to handle elevator operations.

Supporting UML Sequence Diagram:

sequenceDiagram participant E as Elevator participant API as ElevatorAPI participant ZMQ as ZMQ Client loop Every Update Cycle E->>E: Check if floor_changed alt Floor changed E->>E: Set arrival_time, reset flags end alt Is moving E->>E: Check if travel time elapsed alt Time elapsed E->>E: Calculate next_floor (skip floor 0) E->>E: _set_floor(next_floor) end E->>E: Check if arrived at target alt Arrived and announcement delay passed E->>E: _handle_arrival_at_target_floors() E->>API: send_floor_arrived_message() API->>ZMQ: floor_arrived message end else Not moving alt Door state transitions E->>E: Handle OPENING/CLOSING/timeout alt Door opened E->>API: send_door_opened_message() API->>ZMQ: door_opened message else Door closed E->>API: send_door_closed_message() API->>ZMQ: door_closed message E->>E: request_movement_if_needed() end end alt At target floor with closed doors E->>E: open_door() E->>E: Remove task from queue end alt Has remaining tasks E->>E: request_movement_if_needed() end end end

request_movement_if_needed(self) -> None

Functional Description: Initiates elevator movement if there are target floors and doors are closed.

_set_floor(self, new_floor: int) -> None

Functional Description: Updates elevator's current floor and sets floor_changed flag.

_set_moving_state(self, direction_value: str) -> None

Functional Description: Sets elevator's movement state and updates timestamps.

_is_moving(self) -> bool

Functional Description: Returns True if elevator is in MOVING_UP or MOVING_DOWN state.

_get_movement_direction(self) -> int

Functional Description: Returns movement direction as integer (1 for up, -1 for down, 0 for idle).

open_door(self) -> None

Functional Description: Opens elevator doors if not already open/closing and not moving.

close_door(self) -> None

Functional Description: Closes elevator doors if not already closed/opening and not moving.

_determine_direction(self) -> None

Functional Description: Determines optimal movement direction based on task queue.

calculate_estimated_time(self, floor: int, direction: Optional[MoveDirection]) -> float

Functional Description: Calculates estimated time to serve a floor request considering current state and existing tasks.

Supporting UML Sequence Diagram:

sequenceDiagram participant D as Dispatcher participant E as Elevator D->>E: calculate_estimated_time(floor, direction) alt Already at floor with open doors E->>D: return 0 else E->>E: Calculate door closing time if needed alt Elevator is IDLE E->>E: Calculate direct travel time E->>E: Add door operation time E->>D: return total_time else Elevator is moving E->>E: Simulate serving existing tasks E->>E: Check if target reached during simulation alt Target reached with correct direction E->>D: return simulated_time else Target not reached E->>E: Continue simulation to completion E->>E: Add travel time to target E->>D: return total_time end end end

_handle_arrival_at_target_floor(self, current_time: float) -> None

Functional Description: Handles logic when elevator arrives at a target floor.

reset(self) -> None

Functional Description: Resets elevator to initial state (floor 1, idle, doors closed, empty task queue).

Task Class

__init__(self, floor: int, call_id: Optional[str]) -> None

Functional Description: Initializes a task with target floor and optional call ID for outside calls.

is_outside_call(self) -> bool

Functional Description: Returns True if task represents an outside call (has call_id), False for inside calls.

Call Class

__init__(self, call_id: str, floor: int, direction: Optional[MoveDirection]) -> None

Functional Description: Initializes a call with unique ID, target floor, and direction.

assign_to_elevator(self, elevator_idx: int) -> None

Functional Description: Assigns call to specified elevator and updates state to ASSIGNED.

complete(self) -> None

Functional Description: Marks call as completed.

is_pending(self) -> bool

Functional Description: Returns True if call state is PENDING.

is_assigned(self) -> bool

Functional Description: Returns True if call state is ASSIGNED.

is_completed(self) -> bool

Functional Description: Returns True if call state is COMPLETED.

Components Specifications

S1: Target Floor Implementation

This part here will explain the implementation and click event of floor button in detail.

S1.1 Target floor state Logic Implementation

S1.2 Click Event

Once being clicked, the floor button is considered 'activated' in red and will not respond to further clicking until the elevator has reached the target floor and the button turns back to the 'idle' in blue.

S1.3 Backend Command

User Operation: "select_floor": ["-1#1", "-1#2", "1#1", "1#2", "2#1", "2#2", "3#1", "3#2"]

select_floor@i#k means a user in elevator #k selects to go to the i-th floor.

Corresponding System Events: "floor_arrived":["up","down",""],["-1","1","2","3"],["#1", "#2"]

"up_floor_i_arrived#k", indicating that elevator #k has arrived at the i-th floor while moving upwards. "floor_i_arrived#k",indicating that elevator #k has stopped at the i-th floor.

S2:Call Up/Down Implementation

This part here will explain the implementation and click event of call up/down button in detail.

S2.1: Click Event

Same as floor button, the call up/down button is 'activated' in red and will not respond until the elevator has arrived at the passenger's floor, the button will then turn back to 'idle' in blue.

S2.2: Backend Command

User Operations: "call_down": ["3", "2", "1"] "call_up": ["-1", "1", "2"]

call_up/call_down@i signifies the user at floor i pressing the button to call the elevator to go upwards/downwards.

S3:Door Open/Close Implementation

This part here will explain the implementation and click event of door open/close button in detail.

S3.1 OPEN/CLOSE state Logic Implementation

S3.1.1 Open Button:

S3.1.2 Close Button:

S3.2 Click event

The specific click event of the Open/Close button will be presented in the UML sequence diagram below.

S3.3 Backend Command

User Operations: "open_door": ["#1", "#2"] "close_door": ["#1", "#2"] open_door/close_door#i means open/close the doors of elevator #i

Corresponding System Events "door_opened": ["#1", "#2"] "door_closed": ["#1", "#2"]

door_opened/door_closed#i means the doors of elevator #i have opened/closed

S4: Dispatcher Implementation

This section describes how the Dispatcher efficiently manages elevator operations, handling user requests and assigning them to the most suitable elevator based on real-time conditions.

S4.1 Dispatching Logic

The Dispatcher is responsible for managing and assigning outside floor calls (e.g., call up/down from a landing) to the most suitable elevator. It calculates estimated service times and uses this to assign calls. For all assigned tasks, including internal floor selections made by passengers inside an elevator, the Dispatcher optimizes the elevator's task queue to ensure an efficient travel path. Manual door open/close requests, however, are handled directly by the Elevator objects (via the ElevatorAPI) and do not involve the Dispatcher's assignment or optimization logic.

S4.2 Elevator Assignment Strategy

When a passenger calls an elevator from a floor:

  1. The Dispatcher calculates an estimated service time for each elevator.
  2. The elevator with the shortest estimated time is assigned to the request.
  3. The requested floor is added to that elevator's queue.
  4. The sequence of target floors is optimized according to the elevator's current direction and position.

S4.3 Sequence Optimization Strategy

The Dispatcher employs a modified SCAN algorithm (elevator algorithm) to minimize wait times and reduce unnecessary direction changes:

S5: State Update Implementation

This section will show how each elevator's state is updated in real-time, managing movement progress, door operations, and transitions to ensure they stay in sync with user interactions and operational logic.

S5.1 Timed Movement

The Elevator object's update method, called periodically by the Simulator, manages the elevator's movement. It tracks the time elapsed since movement started and, once a single-floor travel time is complete, updates the elevator's current floor. This process ensures the elevator's position and movement status are accurately reflected in the simulation.

S5.2 Door and State Transitions

Summary Table: Backend Commands and Events

Command/Event Description/When Emitted
select_floor@i#k User in elevator #k selects floor i
call_up@i User at floor i calls elevator up
call_down@i User at floor i calls elevator down
open_door#k User requests to open door of elevator #k
close_door#k User requests to close door of elevator #k
reset System reset to initial state
floor_arrived@i#k Elevator #k arrived at floor i
up_floor_arrived@i#k Elevator #k arrived at floor i moving up
down_floor_arrived@i#k Elevator #k arrived at floor i moving down
door_opened#k Elevator #k door opened
door_closed#k Elevator #k door closed